package project3;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Set;
import project3.StemInfo.StemInstance;

/**Represents a document in our collection.
 * Provides methods for getting text and title of document,
 * as well as implementing the DocumentInfo API.
 *
 * Note that a title will not be 'filled in' unless getDocumentText has been called
 * (Usually done by the InvertedIndex while indexing documents). Until that point
 * the file name is used.
 */
public class Document implements DocumentInfo
{
    private String title;
    private File location;
    private long length;
    private Map<String, IndexEntry> stemEntries;

    /**Builds a document
     * @param file File Location of the document text.
     */
    public Document(File file)
    {
        this.title = file.getName();
        this.location=file;
        stemEntries=new HashMap<String, IndexEntry>();
        length=file.length();
    }

    /** Returns the length of the document stem vector, in the form of:
     * sqrt(sum( foreach stem, (stem relative frequency)^2))
     */
    public double getDocumentVectorLength()
    {
    	double docSum=0;
    	for(Map.Entry<String, IndexEntry> entries : stemEntries.entrySet())
    	{
    		double relFreq=entries.getValue().relativeFrequency;
    		docSum+=relFreq*relFreq;
    	}
    	return Math.sqrt(docSum);
    }

    /** Will return the title of the document: either the file name of the document,
     * or the first line of the document if the first line was not empty, was not
     * the only line, and getDocumentText was called.
     * @return title of the document.
     */
    public String getTitle()
    {
        return title;
    }

    /**Reads the Document Text from the source file.
     * @return Document Text/content.
     */
    public String getDocumentText()
    {
        try
        {
            //scan the file for an EOF, return everything before it.
            Scanner scanner = new Scanner(location);
            scanner.useDelimiter("\\Z");
            String document = scanner.next();
            scanner.close();

            //fill in title
            if(document.indexOf("\n")!=-1)
            {
                String proposedTitle = document.substring(0, document.indexOf("\n")).trim();
                //remove useless bits from test data (using proj. gutenberg for testing
                proposedTitle=proposedTitle.replace("The Project Gutenberg EBook of ", "");
                proposedTitle=proposedTitle.replace("The Project Gutenberg eBook, ", "");
                proposedTitle=proposedTitle.replace("Project Gutenberg's ", "");
                //if first line isn't blank, make it the title
                if(proposedTitle.length()!=0)
                    //by default, substring keeps a reference to the original
                    //character array... all 3 megs of it; this fixes that problem
                    title=location.getName()+ ": " + new String(proposedTitle.toCharArray());
            }
            return document;
        }
        catch (FileNotFoundException ex)
        {
            System.err.println("COULD NOT OPEN FILE: "+location);
        }
        catch(NoSuchElementException nsee)
        {
            System.err.println("EMPTY FILE: "+location);
        }
        return "";
    }

    
    /**Finds the set of all stem instances in the document.
     * This method is not used by our IR implementation, but is implemented
     * so as to meet the definition of the document info interface
     * @param stem A stemmed word, as defined by project3.Stemmer
     * @return Set of all StemInstance of the document
     */
    public Set<StemInstance> getInstances(String stem)
    {	
        Set<StemInstance> stemInstances = new HashSet<StemInstance>();
        int line = 0;
        int column = 0;
        Stemmer stemmer = new Stemmer();
        Tokenizer tokenizer=new Tokenizer();
        Scanner scanner = new Scanner(getDocumentText());
        //for each line in the document
        while(scanner.hasNextLine())
        {
            line++;
            String lineData=scanner.nextLine();
            column=0;
            while(column < lineData.length())
            {
                //assume at is the start of word; find the end of the word (first whitespace)
                int at=column;
                while(at<lineData.length()&&!Character.isWhitespace(lineData.charAt(at)))
                    at++;
                //parse out the word; if it has length==0, we weren't at a word
                String word=lineData.substring(column, at);
                if(word.length()>0)
                {
                    //tokenize and stem word; if it matches the sought word,
                    //  add it to the stem set.
                    word=tokenizer.tokenize(word).get(0);
                    String wordStem=stemmer.stem(word);
                    if(wordStem.equals(stem))
                        stemInstances.add(new StemInstance(location, line, column, wordStem, word));
                }
                //set at/column to the next character (should be first char of word)
                column=++at;
            }
        }
        return stemInstances;
    }
    
    /** Returns a map from stem string to the information regaurding the stem.
     * @return map from stem to information
     */
    public Map<String, IndexEntry> getStemIndexEntries()
    {
        return stemEntries;
    }

    /** @return Number of Unique Stems that appear in the document
     */
    public int getStemCount()
    {
        return stemEntries.size();
    }

    /** @return Length of the document, in bytes
     */
    public long getDocumentLengthInBytes()
    {
        return length;
    }

    /** Returns the title of the document */
    public String toString()
    {
        if(title!=null)
            return title;
        if(location!=null)
            return location.getName();
        return "NO_NAME";
    }
}

